diff --git a/doClean.go b/doClean.go index 4b435b3..dbb2b18 100644 --- a/doClean.go +++ b/doClean.go @@ -44,7 +44,54 @@ func doClean() error { doRepoCleanDevel(repo) } - log.Info("finished attempt at cleaning devel branches") + 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 } @@ -100,7 +147,7 @@ func doRepoCleanUser(repo *gitpb.Repo) error { 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) + // log.Info("USER IS IN DEVEL", repo.GetGoPath(), cmd) err := repo.RunVerbose(cmd) return err } @@ -116,7 +163,7 @@ func doRepoCleanUser(repo *gitpb.Repo) error { 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) + // log.Info("USER IS IN DEVEL", repo.GetGoPath(), cmd) err := repo.RunVerbose(cmd) return err } @@ -143,7 +190,7 @@ func justDeleteTheDevelBranchAlready(repo *gitpb.Repo) error { 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) + // log.Info("DEVEL IS IN REMOTE", repo.GetGoPath(), cmd) err := repo.RunVerbose(cmd) return err } @@ -158,7 +205,7 @@ func justDeleteTheDevelBranchAlready(repo *gitpb.Repo) error { 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) + // log.Info("DEVEL IS IN REMOTE", repo.GetGoPath(), cmd) err := repo.RunVerbose(cmd) return err } diff --git a/doDirty.go b/doDirty.go index d13a31d..f4373d9 100644 --- a/doDirty.go +++ b/doDirty.go @@ -36,7 +36,7 @@ func doDirty() { // this needs to run each time in case repos were added manually by the user // this also verifies that func checkNormalRepoState(repo *gitpb.Repo) error { - if err := repo.MakeLocalDevelBranch(); err != nil { + if _, err := repo.MakeLocalDevelBranch(); err != nil { return err } if repo.GetCurrentBranchName() != repo.GetUserBranchName() { diff --git a/gitconfig_parser.go b/gitconfig_parser.go new file mode 100644 index 0000000..edab556 --- /dev/null +++ b/gitconfig_parser.go @@ -0,0 +1,143 @@ +package main + +// playing with gemini to do simple tasks. it kicked this out + +import ( + "bufio" + "errors" + "fmt" + "os" + "path/filepath" + "strings" +) + +// ParsedGitConfig represents the structure of a .gitconfig file, +// which is a map of section names to a map of their key-value pairs. +// For example: +// +// { +// "user": { +// "name": "John Doe", +// "email": "john.doe@example.com", +// }, +// "core": { +// "editor": "vim", +// }, +// } +type ParsedGitConfig map[string]map[string]string + +// ParseGlobalGitConfig finds and parses the global .gitconfig file for the current user. +// It is platform-agnostic and works on Windows, macOS, and Linux. +// It returns the parsed configuration or an error if the file cannot be found or read. +func ParseGlobalGitConfig() (ParsedGitConfig, error) { + // os.UserHomeDir() is the platform-agnostic way to get the user's home directory. + homeDir, err := os.UserHomeDir() + if err != nil { + return nil, fmt.Errorf("could not get user home directory: %w", err) + } + + // filepath.Join correctly constructs the path for the current OS. + gitConfigPath := filepath.Join(homeDir, ".gitconfig") + + file, err := os.Open(gitConfigPath) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + return nil, fmt.Errorf(".gitconfig file not found at %s", gitConfigPath) + } + return nil, fmt.Errorf("could not open .gitconfig file: %w", err) + } + defer file.Close() + + config := make(ParsedGitConfig) + var currentSection string + scanner := bufio.NewScanner(file) + + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + + // Ignore empty lines and comments + if line == "" || line[0] == '#' || line[0] == ';' { + continue + } + + // Check for a new section + if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") { + sectionName := line[1 : len(line)-1] + // Handle subsections like [remote "origin"] by splitting them. + // For simplicity, we'll just use the full string as the key. + // A more complex parser might create nested maps. + currentSection = strings.TrimSpace(sectionName) + if _, exists := config[currentSection]; !exists { + config[currentSection] = make(map[string]string) + } + continue + } + + // Parse key-value pairs within a section + if currentSection != "" { + // Split by "=". Use SplitN to handle values that might contain "=". + parts := strings.SplitN(line, "=", 2) + if len(parts) == 2 { + key := strings.TrimSpace(parts[0]) + value := strings.TrimSpace(parts[1]) + config[currentSection][key] = value + } + } + } + + if err := scanner.Err(); err != nil { + return nil, fmt.Errorf("error reading .gitconfig file: %w", err) + } + + return config, nil +} + +// GetValue retrieves a specific value from the parsed git config. +// It takes the section and key as input (e.g., "user", "name"). +// It returns the value and a boolean indicating if the key was found. +func (c ParsedGitConfig) GetValue(section, key string) (string, bool) { + if sectionMap, ok := c[section]; ok { + if value, ok := sectionMap[key]; ok { + return value, true + } + } + return "", false +} + +/* +// main function to demonstrate the usage of ParseGlobalGitConfig. +func main() { + config, err := ParseGlobalGitConfig() + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } + + fmt.Println("Successfully parsed global .gitconfig file.") + fmt.Println("-----------------------------------------") + + // Example of using GetValue to retrieve the user's name and email. + userName, found := config.GetValue("user", "name") + if found { + fmt.Printf("User Name: %s\n", userName) + } else { + fmt.Println("User name not found.") + } + + userEmail, found := config.GetValue("user", "email") + if found { + fmt.Printf("User Email: %s\n", userEmail) + } else { + fmt.Println("User email not found.") + } + + fmt.Println("\nFull configuration:") + // Print out the full parsed configuration + for section, keys := range config { + fmt.Printf("[%s]\n", section) + for key, value := range keys { + fmt.Printf(" %s = %s\n", key, value) + } + } +} +*/