diff --git a/argv.go b/argv.go index 152fd64..f5551b3 100644 --- a/argv.go +++ b/argv.go @@ -46,6 +46,7 @@ type ExamineCmd struct { type CleanCmd struct { 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"` } type PatchCmd struct { diff --git a/argvAutoshell.go b/argvAutoshell.go index 00ee746..1cdb613 100644 --- a/argvAutoshell.go +++ b/argvAutoshell.go @@ -24,7 +24,7 @@ func (args) doBashAuto() { usr, _ := user.Current() fmt.Println("user devel master " + usr.Username) case "clean": - fmt.Println("force") + fmt.Println("force user") case "commit": fmt.Println("--all") case "config": diff --git a/doCheckout.go b/doCheckout.go index bbb6a5b..03904e8 100644 --- a/doCheckout.go +++ b/doCheckout.go @@ -3,7 +3,6 @@ package main import ( "fmt" - "go.wit.com/lib/protobuf/forgepb" "go.wit.com/lib/protobuf/gitpb" "go.wit.com/log" ) @@ -15,6 +14,7 @@ var ErrorNotAllReposOnUser error = fmt.Errorf("not all repos on are on the user func IsEverythingOnMaster() error { var total int var count int + var nope int // first make sure every repo is on the master branch all := me.forge.Repos.All() @@ -23,9 +23,11 @@ func IsEverythingOnMaster() error { total += 1 if repo.GetMasterBranchName() == repo.GetCurrentBranchName() { count += 1 + } else { + nope += 1 } } - log.Printf("Master branch check. %d total repos. %d repos on the master branch\n", total, count) + log.Printf("Master branch check. %d total repos. (%d ok) (%d not on master branch)\n", total, count, nope) if total != count { // log.Info(ErrorNotAllReposOnMaster) return ErrorNotAllReposOnMaster @@ -90,27 +92,6 @@ func doGitReset() { } } -/* -func checkoutBranches(repo *gitpb.Repo) error { - dname := repo.GetDevelBranchName() - if dname == "" { - if err := me.forge.MakeDevelBranch(repo); err != nil { - log.Info("verify() no devel branch name", repo.GetGoPath()) - return err - } - configSave = true - } - if repo.GetUserBranchName() == "" { - if err := me.forge.MakeUserBranch(repo); err != nil { - log.Info("verify() no devel branch name", repo.GetGoPath()) - return err - } - configSave = true - } - return nil -} -*/ - func rillCheckoutUser(repo *gitpb.Repo) error { if repo.IsDirty() { // never do dirty repos @@ -147,15 +128,37 @@ func doAllCheckoutUser() error { return nil } +func rillCheckoutDevel(repo *gitpb.Repo) error { + if repo.IsDirty() { + // never do dirty repos + return nil + } + if repo.GetCurrentBranchName() == repo.GetDevelBranchName() { + // repo is already on devel branch + return nil + } + repo.CheckoutDevel() + return nil +} + func doAllCheckoutDevel() error { - me.forge.CheckoutDevel() - // me.forge = forgepb.Init() + me.forge.RillFuncError(rillCheckoutDevel) count := me.forge.RillReload() - log.Info("CHECKOUT DEVEL COUNT", count) if count != 0 { me.forge.ConfigSave() } if err := IsEverythingOnDevel(); err != nil { + // display all repos not on user + me.found = new(gitpb.Repos) + all := me.forge.Repos.SortByFullPath() + for all.Scan() { + repo := all.Next() + if repo.GetCurrentBranchName() != repo.GetDevelBranchName() { + me.found.AppendByGoPath(repo) + } + } + me.forge.PrintHumanTable(me.found) + log.Printf("There are %d repos that are NOT on the devel branch\n", me.found.Len()) return err } return nil @@ -224,11 +227,7 @@ func doCheckout() error { } if argv.Checkout.Devel != nil { - me.forge.CheckoutDevel() - me.forge = forgepb.Init() - me.found = new(gitpb.Repos) - argv.Checkout.Devel.findRepos() - me.forge.PrintHumanTable(me.found) + doAllCheckoutDevel() okExit("") } diff --git a/doClean.go b/doClean.go index 154be88..acc510a 100644 --- a/doClean.go +++ b/doClean.go @@ -22,13 +22,14 @@ func doClean() error { repo := all.Next() if repo.GetCurrentBranchName() != repo.GetMasterBranchName() { // skip this while in devel - // continue + continue } if repo.IsDirty() { continue } if err := doCleanRepo(repo); err != nil { - badRepoExit(repo, err) + log.Info(repo.GetGoPath(), err) + okExit("") } } log.Info("All repos on the master branch are clean") @@ -61,21 +62,25 @@ func doCleanRepo(repo *gitpb.Repo) error { if name == repo.GetUserBranchName() { hasLocal = true if err := doCleanUserBranch(repo, b); err != nil { - log.Info("\tLOCAL BRANCH ERROR user") + log.Info("\tLOCAL BRANCH ERROR user =", name) return err } - log.Info("\tLOCAL BRANCH user") + log.Info("\tLOCAL BRANCH user =", name) continue } if name == repo.GetDevelBranchName() { - if err := doCleanDevelBranch(repo, b); err != nil { - log.Info("\tLOCAL BRANCH ERROR devel") - return err - } - log.Info("\tLOCAL BRANCH devel") + /* + if err := doCleanDevelBranch(repo, b); err != nil { + log.Info("\tLOCAL BRANCH ERROR devel") + return err + } + log.Info("\tLOCAL BRANCH devel") + */ continue } - log.Info("\tlocal branch name unknown:", name, b.Merge, b.Remote) + if err := verifyLocalBranchIsMerged(repo, b); err != nil { + return err + } } if argv.Clean.Force == nil { if hasLocal { @@ -86,7 +91,49 @@ func doCleanRepo(repo *gitpb.Repo) error { } func verifyLocalBranchIsMerged(repo *gitpb.Repo, branch *gitpb.GitBranch) error { - return nil + base := filepath.Base(branch.Name) + log.Info("local branch name unknown:", base, branch.Name, branch.Merge, branch.Remote, repo.GetGoPath()) + + // check if it exists in the origin + if repo.Exists(filepath.Join(".git/refs/remotes/origin", base)) { + err := fmt.Errorf("repo %s ERROR. branch is also remote %s", repo.GetGoPath(), branch.Name) + log.Info(err) + if err := isSafeToDelete(repo, base); err != nil { + log.Info(err) + return err + } + if err := requiresGitPush(repo, base); err != nil { + log.Info(err) + return err + } + err = fmt.Errorf("repo %s BRANCH AND REMOTE CAN BE DELETED %s", repo.GetGoPath(), branch.Name) + log.Info(err) + if argv.Clean.Force != nil { + err = forceDeleteBranch(repo, base) + repo.Reload() + } + return err + } + + if !repo.Exists(filepath.Join(".git/refs/heads", base)) { + log.Info("GitConfig() parse logic error. not really a local branch", base) + me.forge.Repos.Delete(repo) + configSave = true + return nil + } + + // this is only a local branch + if err := isSafeToDelete(repo, base); err != nil { + log.Info(err) + return err + } + err := fmt.Errorf("repo %s BRANCH CAN PROBABLY BE DELETED base=%s fullname=%s", repo.GetGoPath(), base, branch.Name) + log.Info(err) + if argv.Clean.Force != nil { + err = forceDeleteBranch(repo, base) + repo.Reload() + } + return err } func doCleanDevelBranch(repo *gitpb.Repo, branch *gitpb.GitBranch) error { @@ -135,10 +182,11 @@ func doCleanUserBranch(repo *gitpb.Repo, branch *gitpb.GitBranch) error { } log.Info("THIS USER BRANCH IS CLEAN TO DELETE", branch.Name) if argv.Clean.Force != nil { - cmd := []string{"git", "branch", "-D", branch.Name} - _, err := repo.RunVerbose(cmd) - log.Info(err) - return err + err := forceDeleteBranch(repo, branch.Name) + repo.Reload() + if err != nil { + return err + } } } else { log.Info("why is this local only branch a problem?", branch.Name) @@ -157,9 +205,11 @@ func doCleanUserBranch(repo *gitpb.Repo, branch *gitpb.GitBranch) error { log.Info("THIS USER BRANCH IS CLEAN TO DELETE", branch.Name) if argv.Clean.Force != nil { cmd := []string{"git", "branch", "-D", branch.Name} - _, err := repo.RunVerbose(cmd) - log.Info("THE GIT BRANCH DELETE ERROR IS:", err) - return err + if _, err := repo.RunVerbose(cmd); err != nil { + log.Info("THE GIT BRANCH DELETE ERROR IS:", err) + return err + } + return nil } } // return fmt.Errorf("%s repo.CurrentTag is not local: %s. Don't proceed yet", repo.GetGoPath(), repo.CurrentTag.Refname) @@ -189,3 +239,71 @@ func userToDevelRequiresGitPush(repo *gitpb.Repo, branchName string) error { } return fmt.Errorf("user branch not clean to delete. maybe it is? devel might be ahead of user branch. %d %d", missing, b2) } + +// checks against upstream master +func isSafeToDelete(repo *gitpb.Repo, old string) error { + var head string + head = filepath.Join("origin", repo.GetMasterBranchName()) + + if !repo.Exists(filepath.Join(".git/refs/remotes/", head)) { + head = filepath.Join("origin", "HEAD") + } + + b1 := countDiffObjects(repo, old, head) + b2 := countDiffObjects(repo, head, old) + log.Info(old, "vs origin count", b1, b2) + if b1 == 0 && b2 == 0 { + log.Info("isSafeToDelete() SAFE TO DELETE ==", old, b1, head, b2) + return nil + } + if b1 == 0 { + log.Info("isSafeToDelete() SAFE TO DELETE ==", old, b1, head, b2) + return nil + } + if b1 != 0 { + log.Info(old, "vs", head, " count b1 != 0, b2 ==", b2, "b1 =", b1) + log.Info("THIS MEANS THE LOCAL BRANCH NEEDS GIT PUSH TO ORIGIN BRANCH ==", b1) + cmd := repo.ConstructGitDiffLog(old, head) + log.Info("cmd", cmd) + cmd = repo.ConstructGitDiffLog(head, old) + log.Info("cmd", cmd) + if argv.Clean.Force != nil { + // return gitPushStrict(repo, branchName) + } + return fmt.Errorf("user branch not clean to delete. needs git push %d %d", b1, b2) + } + return fmt.Errorf("user branch not clean to delete. needs git push %d %d", b1, b2) +} + +// literally ignore all errors. delete everthing with no checks for now +func forceDeleteBranch(repo *gitpb.Repo, branch string) error { + if repo.IsDirty() { + log.Info(repo.GetGoPath(), "is dirty") + return nil + } + if repo.GetUserBranchName() != branch { + log.Info(repo.GetGoPath(), branch, "is not the user branch", repo.GetUserBranchName()) + return nil + } + configSave = true + + cmd := []string{"git", "branch", "-D", branch} + if _, err := repo.RunVerbose(cmd); err != nil { + log.Info("THE GIT BRANCH DELETE ERROR IS:", err) + // return err + } + log.Info("THIS USER REMOTE BRANCH MUST BE DELETED HERE", branch) + // git push origin --delete jcarr + cmd = []string{"git", "push", "origin", "--delete", branch} + if _, err := repo.RunVerbose(cmd); err != nil { + log.Info("THE GIT BRANCH DELETE ERROR IS:", err) + // return err + } + cmd = []string{"git", "branch", "-D", "--remote", "origin/" + branch} + if _, err := repo.RunVerbose(cmd); err != nil { + log.Info("THE GIT BRANCH DELETE ERROR IS:", err) + // return err + } + // return fmt.Errorf("one at a time %s", repo.GetGoPath()) + return nil +} diff --git a/doExamine.go b/doExamine.go index 8a4ca75..f87b411 100644 --- a/doExamine.go +++ b/doExamine.go @@ -259,12 +259,12 @@ func gitPushStrict(repo *gitpb.Repo, branchName string) error { func requiresGitPush(repo *gitpb.Repo, branchName string) error { b1 := countDiffObjects(repo, branchName, "origin/"+branchName) b2 := countDiffObjects(repo, "origin/"+branchName, branchName) - log.Info("jcarr vs origin count", b1, b2) + log.Info(branchName, "vs origin count", b1, b2) if b1 == 0 && b2 == 0 { return nil } if b1 != 0 { - log.Info("jcarr vs origin count b1 != 0, b2 ==", b1, b2) + 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.Examine.Fix != nil { return gitPushStrict(repo, branchName)