forge/doClean.go

218 lines
5.7 KiB
Go

// 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"
)
// reverts all repos back to the original master branches
// automatically deletes local devel and user branches
func doClean() error {
// fix this to work, then delete all the other options for "forge clean'
if err := doAllCheckoutMaster(); err != nil {
// badExit(err)
}
all := me.forge.Repos.SortByFullPath()
for all.Scan() {
repo := all.Next()
if repo.GetCurrentBranchName() != repo.GetMasterBranchName() {
continue
}
if repo.IsDirty() {
continue
}
// when publishing, clean out the details of that if it's still there
if repo.GetTargetVersion() != "" {
repo.SetTargetVersion("")
configSave = true
}
// try to delete user
if err := doRepoCleanUser(repo); err != nil {
log.Info(repo.GetGoPath(), err)
}
// try to delete devel
doRepoCleanDevel(repo)
}
found := gitpb.NewRepos()
total := 0
// find all repos that aren't "clean"
all = me.forge.Repos.SortByFullPath()
for all.Scan() {
repo := all.Next()
total += 1
// find repos not on master branch
if repo.GetCurrentBranchName() != repo.GetMasterBranchName() {
found.AppendByGoPath(repo)
continue
}
// find dirty repos
if repo.IsDirty() {
found.AppendByGoPath(repo)
continue
}
// find repos that still have a local user branch
if repo.IsLocalBranch(repo.GetUserBranchName()) {
found.AppendByGoPath(repo)
continue
}
// find repos that still have a local devel branch
if repo.IsLocalBranch(repo.GetDevelBranchName()) {
found.AppendByGoPath(repo)
continue
}
}
// display all the repos that aren't clean to the user
log.Printf("\n") // padding for now
if argv.Verbose {
me.forge.PrintHumanTableDirty(found)
} else {
me.forge.PrintHumanTable(found)
}
log.Printf("\n") // padding for now
if found.Len() == 0 {
log.Printf("%d repos are not clean\n", found.Len())
} else {
log.Info("All repos are clean", total, found.Len())
}
return nil
}
/*
func doesLocalBranchExist(repo *gitpb.Repo, branch string) bool {
return repo.Exists(filepath.Join(".git/refs/heads", branch))
}
*/
func doRepoCleanDevel(repo *gitpb.Repo) error {
if !repo.IsLocalBranch(repo.GetDevelBranchName()) {
// there is no local branch named 'devel'
return nil
}
if repo.GetCurrentBranchName() != repo.GetMasterBranchName() {
return log.Errorf("%s not on master branch:", repo.GetFullPath())
}
if repo.IsDirty() {
return log.Errorf("%s is dirty:", repo.GetFullPath())
}
if err := justDeleteTheDevelBranchAlready(repo); err != nil {
log.Info("justDeleteTheDevel() err", repo.GetGoPath(), err)
configSave = true
return err
}
return nil
}
// removes all local user branches
func doRepoCleanUser(repo *gitpb.Repo) error {
if repo.IsDirty() {
return nil
}
bruser := repo.GetUserBranchName()
brdevel := repo.GetDevelBranchName()
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")
}
if !repo.IsLocalBranch(bruser) {
// there is no local user branch
return nil
}
// will you loose work if you delete your user branch?
// if DevelBranchExists()
// then if UserBranchCommits exist in DevelBranch
// DeleteUserBranch is safe
if repo.IsLocalBranch(brdevel) {
b1 := repo.CountDiffObjects(bruser, "refs/heads/"+brdevel) // should be zero
if b1 == 0 {
// every user branch exists in devel. delete user branch
cmd := []string{"git", "branch", "-D", bruser}
// log.Info("USER IS IN DEVEL", repo.GetGoPath(), cmd)
err := repo.RunVerbose(cmd)
return err
}
}
brmaster := repo.GetMasterBranchName()
// will you loose work if you delete your user branch?
// if master branch exists()
// then if all user commits exist in master
// delete user branch is safe
if repo.IsLocalBranch(brmaster) {
b1 := repo.CountDiffObjects(bruser, "refs/heads/"+brmaster) // 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 unique commits", bruser)
}
// 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, "refs/heads/"+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
}