check devel branches

This commit is contained in:
Jeff Carr 2025-01-19 16:07:17 -06:00
parent 4c38b052fd
commit 835c000aef
7 changed files with 185 additions and 46 deletions

View File

@ -45,8 +45,13 @@ type ExamineCmd struct {
} }
type CleanCmd struct { type CleanCmd struct {
Force *EmptyCmd `arg:"subcommand:force" help:"dangerously delete things that are not pushed upstream"` Force *EmptyCmd `arg:"subcommand:force" help:"dangerously delete things that are not pushed upstream"`
User *EmptyCmd `arg:"subcommand:user" help:"dump all the user branches"` User *EmptyCmd `arg:"subcommand:user" help:"clean the user branches"`
Devel *CleanDevelCmd `arg:"subcommand:devel" help:"clean and verify the devel branches"`
}
type CleanDevelCmd struct {
Force bool `arg:"--force" help:"try to strong arm things"`
} }
type PatchCmd struct { type PatchCmd struct {

View File

@ -24,7 +24,7 @@ func (args) doBashAuto() {
usr, _ := user.Current() usr, _ := user.Current()
fmt.Println("user devel master " + usr.Username) fmt.Println("user devel master " + usr.Username)
case "clean": case "clean":
fmt.Println("force user") fmt.Println("devel user force")
case "commit": case "commit":
fmt.Println("--all") fmt.Println("--all")
case "config": case "config":

View File

@ -11,7 +11,7 @@ var ErrorNotAllReposOnMaster error = fmt.Errorf("not all repos on are on the mas
var ErrorNotAllReposOnDevel error = fmt.Errorf("not all repos on are on the devel branch") var ErrorNotAllReposOnDevel error = fmt.Errorf("not all repos on are on the devel branch")
var ErrorNotAllReposOnUser error = fmt.Errorf("not all repos on are on the user branch") var ErrorNotAllReposOnUser error = fmt.Errorf("not all repos on are on the user branch")
func IsEverythingOnMaster() error { func IsEverythingOnMaster() (int, int, error) {
var total int var total int
var count int var count int
var nope int var nope int
@ -30,12 +30,12 @@ func IsEverythingOnMaster() error {
log.Printf("Master branch check. %d total repos. (%d ok) (%d not on master branch)\n", total, count, nope) log.Printf("Master branch check. %d total repos. (%d ok) (%d not on master branch)\n", total, count, nope)
if total != count { if total != count {
// log.Info(ErrorNotAllReposOnMaster) // log.Info(ErrorNotAllReposOnMaster)
return ErrorNotAllReposOnMaster return total, count, ErrorNotAllReposOnMaster
} }
return nil return total, count, nil
} }
func IsEverythingOnDevel() error { func IsEverythingOnDevel() (int, int, error) {
var total int var total int
var count int var count int
@ -50,12 +50,12 @@ func IsEverythingOnDevel() error {
} }
log.Printf("Devel branch check. %d total repos. %d repos on the devel branch\n", total, count) log.Printf("Devel branch check. %d total repos. %d repos on the devel branch\n", total, count)
if total != count { if total != count {
return ErrorNotAllReposOnDevel return total, count, ErrorNotAllReposOnDevel
} }
return nil return total, count, nil
} }
func IsEverythingOnUser() error { func IsEverythingOnUser() (int, int, error) {
var total int var total int
var count int var count int
@ -70,9 +70,9 @@ func IsEverythingOnUser() error {
} }
log.Printf("User branch check. %d total repos. %d repos on the user branch\n", total, count) log.Printf("User branch check. %d total repos. %d repos on the user branch\n", total, count)
if total != count { if total != count {
return ErrorNotAllReposOnUser return total, count, ErrorNotAllReposOnUser
} }
return nil return total, count, nil
} }
func doGitReset() { func doGitReset() {
@ -113,7 +113,7 @@ func doAllCheckoutUser() error {
if count != 0 { if count != 0 {
me.forge.ConfigSave() me.forge.ConfigSave()
} }
if err := IsEverythingOnUser(); err != nil { if _, _, err := IsEverythingOnUser(); err != nil {
// display all repos not on user // display all repos not on user
me.found = new(gitpb.Repos) me.found = new(gitpb.Repos)
all := me.forge.Repos.SortByFullPath() all := me.forge.Repos.SortByFullPath()
@ -149,7 +149,7 @@ func doAllCheckoutDevel() error {
if count != 0 { if count != 0 {
me.forge.ConfigSave() me.forge.ConfigSave()
} }
if err := IsEverythingOnDevel(); err != nil { if _, _, err := IsEverythingOnDevel(); err != nil {
// display all repos not on user // display all repos not on user
me.found = new(gitpb.Repos) me.found = new(gitpb.Repos)
all := me.forge.Repos.SortByFullPath() all := me.forge.Repos.SortByFullPath()
@ -206,7 +206,7 @@ func doAllCheckoutMaster() error {
me.forge.ConfigSave() me.forge.ConfigSave()
} }
if err := IsEverythingOnMaster(); err != nil { if _, _, err := IsEverythingOnMaster(); err != nil {
// display all repos not on master // display all repos not on master
me.found = new(gitpb.Repos) me.found = new(gitpb.Repos)
all := me.forge.Repos.SortByFullPath() all := me.forge.Repos.SortByFullPath()

View File

@ -10,41 +10,129 @@ import (
) )
var ErrorReposHasLocalBranches error = fmt.Errorf("repo still has local branches") 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 { func doClean() error {
if err := IsEverythingOnMaster(); err != nil { if argv.Clean.Devel != nil {
log.Info("Not all repos are on the master branch") if err := doCleanDevel(); err != nil {
// return err 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 {
all := me.forge.Repos.SortByFullPath() all := me.forge.Repos.SortByFullPath()
for all.Scan() { for all.Scan() {
repo := all.Next() repo := all.Next()
if repo.GetCurrentBranchName() != repo.GetMasterBranchName() { if _, count, err := IsEverythingOnMaster(); err != nil {
// skip this while in devel if count == 0 {
continue log.Info("No repos are on the master branch")
return nil
}
log.Info("Not all repos are on the master branch")
// return err
} }
if repo.IsDirty() {
continue if err := doCleanUserRepo(repo); err != nil {
}
if err := doCleanRepo(repo); err != nil {
log.Info(repo.GetGoPath(), err) log.Info(repo.GetGoPath(), err)
okExit("") return err
} }
} }
log.Info("All repos on the master branch are clean") return nil
}
func doCleanDevel() error {
var total int
var count int
all := me.forge.Repos.SortByFullPath()
for all.Scan() {
repo := all.Next()
if argv.Verbose {
log.Info("Cleaning:", repo.GetGoPath())
}
total += 1
if repo.GetCurrentBranchName() != repo.GetDevelBranchName() {
// only process branches in devel
// return nil
}
if repo.IsDirty() {
return nil
}
count += 1
if err := doCleanDevelRepo(repo); err != nil {
log.Info(repo.GetGoPath(), err)
return err
}
}
log.Printf("attempted cleaning %d branches of %d total branches\n", count, total)
return nil
}
func doCleanDevelRepo(repo *gitpb.Repo) error {
devel := repo.GetDevelBranchName()
// log.Printf("%s Start verify devel branch: %s\n", repo.GetGoPath(), devel)
// check if devel branch exists in remote repo
if repo.Exists(filepath.Join(".git/refs/remotes/origin", devel)) {
remote := filepath.Join("origin", devel)
if err := isBranchSubsetOfTrunk(repo, devel, remote); err != nil {
if err == ErrorMergeBranch {
log.Info("can not do this yet. need push to upstream", repo.GetGoPath())
return nil
}
return err
}
// log.Info("todo: verify against remote devel branch", repo.GetGoPath())
}
// verify devel branch is subset of master branch
master := repo.GetMasterBranchName()
if err := isBranchSubsetOfTrunk(repo, devel, master); err != nil {
if err == ErrorMergeBranch {
if argv.Force {
if repo.GetCurrentBranchName() == devel {
cmd := []string{"git", "merge", master}
// only run this if branch is local
_, err := repo.RunVerbose(cmd)
return err
} else {
cmd := []string{"git", "merge", master}
log.Info("can't run. on wrong branch.", cmd, repo.GetGoPath(), "current branch =", repo.GetCurrentBranchName())
}
}
return nil
}
return err
}
// log.Info("todo: verify against remote devel branch", repo.GetGoPath())
return nil return nil
} }
// removes all local branches // removes all local branches
func doCleanRepo(repo *gitpb.Repo) error { func doCleanUserRepo(repo *gitpb.Repo) error {
var hasLocal bool var hasLocal bool
if argv.Verbose {
log.Info("Cleaning:", repo.GetGoPath())
}
if repo.GitConfig == nil { if repo.GitConfig == nil {
return fmt.Errorf("GitConfig == nil") return fmt.Errorf("GitConfig == nil")
} }
if repo.GetCurrentBranchName() != repo.GetMasterBranchName() {
// skip this while in devel
return nil
}
if repo.IsDirty() {
return nil
}
for _, l := range repo.GitConfig.Local { for _, l := range repo.GitConfig.Local {
log.Info("\tlocal branch name:", l.Name) log.Info("\tlocal branch name:", l.Name)
@ -83,13 +171,9 @@ func doCleanRepo(repo *gitpb.Repo) error {
continue continue
} }
if name == repo.GetDevelBranchName() { if name == repo.GetDevelBranchName() {
/* continue
if err := doCleanDevelBranch(repo, b); err != nil { }
log.Info("\tLOCAL BRANCH ERROR devel") if name == repo.GetMasterBranchName() {
return err
}
log.Info("\tLOCAL BRANCH devel")
*/
continue continue
} }
if err := verifyLocalBranchIsMerged(repo, b); err != nil { if err := verifyLocalBranchIsMerged(repo, b); err != nil {
@ -150,11 +234,6 @@ func verifyLocalBranchIsMerged(repo *gitpb.Repo, branch *gitpb.GitBranch) error
return err return err
} }
func doCleanDevelBranch(repo *gitpb.Repo, branch *gitpb.GitBranch) error {
log.Printf("\tDo something %s on branch name:%s merge:%s remote:%s\n", repo.GetGoPath(), branch.Name, branch.Merge, branch.Remote)
return nil
}
func doCleanUserBranch(repo *gitpb.Repo, branch *gitpb.GitBranch) error { func doCleanUserBranch(repo *gitpb.Repo, branch *gitpb.GitBranch) error {
if branch.Name != repo.GetUserBranchName() { if branch.Name != repo.GetUserBranchName() {
return fmt.Errorf("repo %s was not user branch %s", repo.GetGoPath(), branch.Name) return fmt.Errorf("repo %s was not user branch %s", repo.GetGoPath(), branch.Name)
@ -364,3 +443,38 @@ func BADforceDeleteBranch(repo *gitpb.Repo, branch string) error {
// return fmt.Errorf("one at a time %s", repo.GetGoPath()) // return fmt.Errorf("one at a time %s", repo.GetGoPath())
return nil return nil
} }
// verifies that the branch is a pure subset of the other branch
// sorry about the 'master' 'slave' nameing thing. I guess that isn't
// 'cool' to use anymore. I can't think of other terms that aren't reserved words.
func isBranchSubsetOfTrunk(repo *gitpb.Repo, branch string, trunk string) error {
b1 := countGitDiffLog(repo, branch, trunk) // should be zero
b2 := countGitDiffLog(repo, trunk, branch) // can be greater than 1
// log.Info(branch, "vs", trunk, "count", b1, b2)
if b1 == 0 && b2 == 0 {
// log.Info("branch and trunk are identical ==", branch, b1, trunk, b2)
return nil
}
if b1 == 0 {
cmd := []string{"git", "merge", trunk}
log.Printf("%-40s branch %s needs merge with trunk %s len(%d) %s\n", repo.GetGoPath(), branch, trunk, b2, cmd)
return ErrorMergeBranch
}
if b2 == 0 {
log.Printf("%-40s trunk %s needs merge with branch %s len(%d)\n", repo.GetGoPath(), branch, trunk, b2)
return ErrorMergeTrunk
}
return fmt.Errorf("branch not clean to delete. needs merge %d %d", b1, b2)
}
// count all objects only in branch1
// if zero, that means branch1 is entirely contained in branch2 and can be safely deleted
func countGitDiffLog(repo *gitpb.Repo, branch1, branch2 string) int {
cmd := repo.ConstructGitDiffLog(branch1, branch2)
r, err := repo.RunStrictNew(cmd)
if err != nil {
return -1
}
// log.Info("countDiffObjects()", cmd, len(r.Stdout), strings.Join(r.Stdout, " "))
return len(r.Stdout)
}

View File

@ -29,3 +29,23 @@ func doGitPull() {
} }
} }
// git fetch origin master:master
func rillFetchMaster(repo *gitpb.Repo) error {
if repo.GetCurrentBranchName() != repo.GetUserBranchName() {
// only fetch when branch is on the user branch
return nil
}
branch := repo.GetMasterBranchName()
cmd := []string{"git", "fetch", "origin", branch + ":" + branch}
_, err := repo.RunVerbose(cmd)
return err
}
func doGitFetch() {
me.forge.RillFuncError(rillFetchMaster)
count := me.forge.RillReload()
if count != 0 {
me.forge.ConfigSave()
}
}

View File

@ -136,8 +136,8 @@ func main() {
} }
if argv.GitPull != nil { if argv.GitPull != nil {
argv.GitPull.findRepos() // argv.GitPull.findRepos()
doGitPull() doGitFetch()
okExit("") okExit("")
} }

View File

@ -104,7 +104,7 @@ func submitPatchesBox(box *gui.Node) *patchSummary {
if err != nil { if err != nil {
return return
} }
if err := IsEverythingOnDevel(); err != nil { if _, _, err := IsEverythingOnDevel(); err != nil {
log.Info("You can only apply patches to the devel branch") log.Info("You can only apply patches to the devel branch")
return return
} }