repostatus/gitConfig.go

295 lines
7.1 KiB
Go

package repostatus
import (
"bufio"
"os"
"path/filepath"
"strings"
"go.wit.com/log"
)
// GitConfig represents the parsed .git/config data
// type GitConfig map[string]map[string]string
type remote struct {
url string
fetch string
}
type branch struct {
remote string
merge string
}
type GitConfig struct {
core map[string]string // map[origin] = "https:/git.wit.org/gui/gadgets"
remotes map[string]*remote // map[origin] = "https:/git.wit.org/gui/gadgets"
branches map[string]*branch // map[guimaster] = origin guimaster
submodules map[string]string
hashes map[string]string
versions map[string]string
}
type GoConfig map[string]string
func ListGitDirectories() []string {
var all []string
homeDir, err := os.UserHomeDir()
if err != nil {
log.Log(WARN, "Error getting home directory:", err)
return nil
}
srcDir := filepath.Join(homeDir, "go/src")
err = filepath.Walk(srcDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
log.Log(WARN, "Error accessing path:", path, err)
return nil
}
// Check if the current path is a directory and has a .git subdirectory
if info.IsDir() && IsGitDir(path) {
all = append(all, path)
// fmt.Println(path)
}
return nil
})
if err != nil {
log.Log(WARN, "Error walking the path:", srcDir, err)
}
return all
}
// IsGitDir checks if a .git directory exists inside the given directory
func IsGitDir(dir string) bool {
gitDir := filepath.Join(dir, ".git")
info, err := os.Stat(gitDir)
if os.IsNotExist(err) {
return false
}
return info.IsDir()
}
// readGitConfig reads and parses the .git/config file
func (rs *RepoStatus) readGitConfig() error {
filename := filepath.Join(rs.realPath.String(), "/.git/config")
file, err := os.Open(filename)
if err != nil {
log.Log(WARN, "readGitConfig() failed for file:", filename)
filename = filepath.Join(rs.realPath.String(), "../.git/config")
log.Log(WARN, "readGitConfig() trying up one directory instead", filename)
file, err = os.Open(filename)
if err != nil {
return err
}
}
defer file.Close()
var currentSection string = ""
var currentName string = ""
rs.gitConfig = new(GitConfig)
rs.gitConfig.core = make(map[string]string)
rs.gitConfig.remotes = make(map[string]*remote)
rs.gitConfig.branches = make(map[string]*branch)
rs.gitConfig.submodules = make(map[string]string)
rs.gitConfig.versions = make(map[string]string)
rs.gitConfig.hashes = make(map[string]string)
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
// Skip empty lines and comments
if line == "" || strings.HasPrefix(line, "#") || strings.HasPrefix(line, ";") {
continue
}
// Check for section headers
if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
line = strings.Trim(line, "[]")
parts := strings.Split(line, " ")
currentSection = parts[0]
if len(parts) == 2 {
line = strings.Trim(line, "[]")
currentName = strings.Trim(parts[1], "\"")
}
continue
}
partsNew := strings.SplitN(line, "=", 2)
if len(partsNew) != 2 {
log.Log(WARN, "error on config section:", currentSection, "line:", line)
}
key := strings.TrimSpace(partsNew[0])
key = strings.TrimSuffix(key, "\"")
value := strings.TrimSpace(partsNew[1])
value = strings.TrimSuffix(value, "\"")
switch currentSection {
case "core":
rs.gitConfig.core[key] = value
case "pull":
// don't store git config pull settings here
// probably has 'rebase = false'
case "remote":
test, ok := rs.gitConfig.remotes[currentName]
if !ok {
test = new(remote)
rs.gitConfig.remotes[currentName] = test
}
log.Log(INFO, "switch currentSection", currentSection, currentName)
switch key {
case "url":
if test.url == value {
continue
}
if test.url == "" {
test.url = value
continue
}
log.Log(REPO, "error url mismatch", test.url, value)
case "fetch":
if test.fetch == value {
continue
}
if test.fetch == "" {
test.fetch = value
continue
}
log.Log(REPO, "error fetch mismatch", test.fetch, value)
default:
log.Log(REPO, "unknown remote:", rs.Path(), line)
}
case "branch":
test, ok := rs.gitConfig.branches[currentName]
if !ok {
test = new(branch)
rs.gitConfig.branches[currentName] = test
rs.processBranch(currentName)
}
switch key {
case "remote":
rs.gitConfig.branches[currentName].remote = value
case "merge":
rs.gitConfig.branches[currentName].merge = value
default:
log.Log(REPO, "error unknown remote:", currentSection, currentName, key, value)
log.Log(REPO, "unknown branch:", rs.Path(), line)
}
case "submodule":
// test, ok := rs.gitConfig.submodules[currentName]
switch key {
case "active":
// probably 'true' or 'false'
case "url":
rs.gitConfig.submodules[currentName] = value
default:
log.Log(REPOWARN, "unknown submodule line:", rs.Path(), line)
}
default:
log.Log(REPOWARN, "unknown line:", rs.Path(), line)
}
}
if err := scanner.Err(); err != nil {
return err
}
return nil
}
func (rs *RepoStatus) GitURL() string {
origin, ok := rs.gitConfig.remotes["origin"]
if ok {
return origin.url
}
for i, s := range rs.gitConfig.remotes {
log.Log(WARN, "remote:", i, s.url)
}
log.Log(WARN, "GitURL() repo has non-standard origin or is not uploaded")
return ""
}
func (rs *RepoStatus) GitLsFiles() (bool, string) {
err, output := rs.RunCmd([]string{"git", "ls-files"})
if err != nil {
log.Warn("git ls-files failed err =", err)
log.Warn("git ls-files failed output =", output)
return false, output
}
return true, output
}
/*
func (rs *RepoStatus) Writable() {
rs.readOnly.SetText("false")
}
*/
func (rs *RepoStatus) ReadOnly() bool {
if rs.readOnly.String() == "true" {
return true
} else {
return false
}
}
func (rs *RepoStatus) SetReadOnly(b bool) {
if b {
rs.readOnly.SetText("true")
} else {
rs.readOnly.SetText("false")
}
}
func (rs *RepoStatus) processBranch(branch string) {
fullpath := rs.realPath.String()
log.Log(INFO, " ", branch)
hash, ok := rs.gitConfig.hashes[branch]
filename := fullpath + "/.git/refs/heads/" + branch
log.Log(INFO, " hash: need to open", filename)
newhash, err := readFileToString(filename)
if err != nil {
log.Log(WARN, "hash: read failed", filename, rs.String())
return
}
log.Log(INFO, " hash:", newhash)
rs.gitConfig.hashes[branch] = newhash
if ok {
if hash != newhash {
log.Log(WARN, "hash changed", hash, rs.String())
}
}
name, _ := rs.gitDescribeByHash(newhash)
rs.gitConfig.versions[newhash] = name
log.Log(INFO, " hash: version", name)
}
func (rs *RepoStatus) BranchExists(branch string) bool {
hash, ok := rs.gitConfig.hashes[branch]
if ok {
log.Log(REPOWARN, rs.Path(), "found branch", branch, hash)
return true
}
for i, t := range rs.Tags.tags {
base := filepath.Base(t.tag.String())
if base == branch {
log.Info("found tag:", i, t.tag.String())
return true
}
log.Info("not tag:", i, t.tag.String())
}
log.Log(REPOWARN, rs.Path(), "did not find branch", branch)
return false
}