diff --git a/gitTag.proto b/gitTag.proto index baae552..dbc28de 100644 --- a/gitTag.proto +++ b/gitTag.proto @@ -4,6 +4,25 @@ package gitpb; import "google/protobuf/timestamp.proto"; // Import the well-known type for Timestamp +message GitRemote { // `autogenpb:nomutex` + string url = 1; + string fetch = 2; +} + +message GitBranch { // `autogenpb:nomutex` + string remote = 1; + string merge = 2; +} + +// readGitConfig reads and parses the .git/config file +message GitConfig { // `autogenpb:nomutex` + map core = 1; // map[origin] = "https:/git.wit.org/gui/gadgets" + map remotes = 2; // map[origin] = "https:/git.wit.org/gui/gadgets" + map branches = 3; // map[guimaster] = origin guimaster + map submodules = 4; + map hashes = 5; + map versions = 6; +} message GitTag { // `autogenpb:nomutex` string refname = 1; // `autogenpb:unique` `autogenpb:sort` // tag name. treated as unique diff --git a/reload.go b/reload.go index 3ac55c1..bb122c0 100644 --- a/reload.go +++ b/reload.go @@ -28,6 +28,12 @@ func (repo *Repo) Reload() error { repo.CheckDirty() repo.setRepoState() + if repo.GitConfig == nil { + if err := repo.updateGitConfig(); err != nil { + return err + } + } + // LastUpdate should always be the newest time repo.Times.LastUpdate = timestamppb.New(time.Now()) return nil diff --git a/reloadGitConfig.go b/reloadGitConfig.go new file mode 100644 index 0000000..367bd69 --- /dev/null +++ b/reloadGitConfig.go @@ -0,0 +1,176 @@ +package gitpb + +import ( + "bufio" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "go.wit.com/log" +) + +// does processing on the go.mod and go.sum files + +func (repo *Repo) updateGitConfig() error { + if repo == nil { + return fmt.Errorf("gitpb.updateGitConfig() repo == nil") + } + if repo.GitConfig == nil { + repo.GitConfig = new(GitConfig) + } + + repo.GitConfig.Core = make(map[string]string) + repo.GitConfig.Remotes = make(map[string]*GitRemote) + repo.GitConfig.Branches = make(map[string]*GitBranch) + repo.GitConfig.Submodules = make(map[string]string) + repo.GitConfig.Versions = make(map[string]string) + repo.GitConfig.Hashes = make(map[string]string) + return repo.readGitConfig() +} + +// readGitConfig reads and parses the .git/config file +func (repo *Repo) readGitConfig() error { + filename := filepath.Join(repo.GetFullPath(), ".git/config") + file, err := os.Open(filename) + defer file.Close() + if err != nil { + return err + } + + var currentSection string = "" + var currentName 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": + repo.GitConfig.Core[key] = value + case "gui": + // don't really need gui stuff right now + case "pull": + // don't store git config pull settings here + // git config probably has 'rebase = false' + case "remote": + test, ok := repo.GitConfig.Remotes[currentName] + if !ok { + test = new(GitRemote) + repo.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(INFO, "error url mismatch", test.Url, value) + case "fetch": + if test.Fetch == value { + continue + } + if test.Fetch == "" { + test.Fetch = value + continue + } + log.Log(INFO, "error fetch mismatch", test.Fetch, value) + default: + log.Log(INFO, "unknown remote:", line) + } + case "branch": + test, ok := repo.GitConfig.Branches[currentName] + if !ok { + test = new(GitBranch) + repo.GitConfig.Branches[currentName] = test + repo.processBranch(currentName) + } + switch key { + case "remote": + repo.GitConfig.Branches[currentName].Remote = value + case "merge": + repo.GitConfig.Branches[currentName].Merge = value + default: + log.Log(INFO, "error unknown remote:", currentSection, currentName, key, value) + log.Log(INFO, "unknown branch:", line) + } + case "submodule": + // test, ok := rs.gitConfig.submodules[currentName] + switch key { + case "active": + // probably 'true' or 'false' + case "url": + repo.GitConfig.Submodules[currentName] = value + default: + log.Log(WARN, "unknown submodule line:", line) + } + default: + log.Log(WARN, "unknown line:", line) + } + } + + if err := scanner.Err(); err != nil { + return err + } + + return nil +} + +func (repo *Repo) processBranch(branch string) { + log.Log(INFO, " ", branch) + hash, ok := repo.GitConfig.Hashes[branch] + filename := filepath.Join(repo.GetFullPath() + "/.git/refs/heads/" + branch) + log.Log(INFO, " hash: need to open", filename) + + data, err := ioutil.ReadFile(filename) + if err != nil { + log.Log(WARN, "hash: read failed", filename) + return + } + newhash := strings.TrimSpace(string(data)) + log.Log(INFO, " hash:", newhash) + repo.GitConfig.Hashes[branch] = newhash + if ok { + if hash != newhash { + log.Log(WARN, "hash changed", hash) + } + } + + name, _ := repo.gitDescribeByHash(newhash) + repo.GitConfig.Versions[newhash] = name + log.Log(INFO, " hash: version", name) +} diff --git a/reloadMtime.go b/reloadMtime.go index 7c18e9f..99869a7 100644 --- a/reloadMtime.go +++ b/reloadMtime.go @@ -119,6 +119,31 @@ func (repo *Repo) changedHead() bool { return true } +// check the mtime of the .git/config file +func (repo *Repo) changedConfig() bool { + fname := ".git/config" + fileTime := repo.Mtime(fname) + if fileTime == nil { + // .git/config doesn't exist. something is wrong! + log.Info("gitpb .git/config is missing", repo.GetGoPath()) + return false + } + mtime := timestamppb.New(*fileTime) + pbtime := repo.Times.MtimeConfig + if pbtime == nil { // this can happen? + repo.Times.MtimeConfig = mtime + return true + } + + if (pbtime.Seconds == mtime.Seconds) && (pbtime.Nanos == mtime.Nanos) { + return false + } + dur := mtime.AsTime().Sub(pbtime.AsTime()) + repo.StateChange = fmt.Sprintf("%s changed %s", fname, shell.FormatDuration(dur)) + repo.Times.MtimeConfig = mtime + return true +} + func (repo *Repo) changedIndex() bool { fname := ".git/index" fileTime := repo.Mtime(fname) @@ -154,6 +179,9 @@ func (repo *Repo) reloadMtimes() bool { if repo.changedIndex() { changed = true } + if repo.changedConfig() { + changed = true + } if repo.changedDir() { // changed = true } @@ -171,6 +199,9 @@ func (repo *Repo) DidRepoChange() bool { if repo.didFileChange(".git/index", repo.Times.MtimeIndex) { return true } + if repo.didFileChange(".git/config", repo.Times.MtimeConfig) { + return true + } if repo.didFileChange(".git", repo.Times.MtimeDir) { // todo: do something with CheckDirty() // return true diff --git a/reloadTags.go b/reloadTags.go index eb4731e..3b574ac 100644 --- a/reloadTags.go +++ b/reloadTags.go @@ -135,21 +135,28 @@ func (repo *Repo) NewestTag() *GitTag { return nil } +// this should just do is.Exists(".git/refs/heads/findname") func (repo *Repo) LocalTagExists(findname string) bool { - loop := repo.Tags.SortByRefname() - for loop.Scan() { - ref := loop.Next() - // log.Info(repo.GoPath, ref.Refname) - if strings.HasPrefix(ref.Refname, "refs/remotes") { - continue - } - _, tagname := filepath.Split(ref.Refname) - // log.Info("tag:", path, tagname, "from", repo.GoPath) - if tagname == findname { - // log.Info("found tag:", path, tagname, "from", repo.GoPath) - return true - } + fname := filepath.Join(".git/refs/heads", findname) + if repo.Exists(fname) { + return true } + /* + loop := repo.Tags.SortByRefname() + for loop.Scan() { + ref := loop.Next() + // log.Info(repo.GoPath, ref.Refname) + if strings.HasPrefix(ref.Refname, "refs/remotes") { + continue + } + tagname := filepath.Base(ref.Refname) + // log.Info("tag:", path, tagname, "from", repo.GoPath) + if tagname == findname { + // log.Info("found tag:", path, tagname, "from", repo.GoPath) + return true + } + } + */ return false } diff --git a/repo.proto b/repo.proto index 80583b9..a21b899 100644 --- a/repo.proto +++ b/repo.proto @@ -35,6 +35,7 @@ message Repo { // `autogenpb:marshal` `autogenpb:2nomutex` repeated string dirtyList = 22; // store the list from git status --porcelain string state = 23; // status or state. useful for building tooling GitTag currentTag = 24; // used to examine repo branches + GitConfig gitConfig = 25; // protobuf of the current .git/config } message Repos { // `autogenpb:marshal` `autogenpb:sort` `autogenpb:2nomutex` @@ -54,6 +55,7 @@ message GitTimes { // `autogenpb:2nomutex` google.protobuf.Timestamp mtimeFetch = 7; // mtime for ./git/FETCH_HEAD // last time 'git fetch' or 'git pull' was run on current branch? google.protobuf.Timestamp lastGoDep = 8; // mtime for last go.sum scan google.protobuf.Timestamp newestCommit = 9; // when the newest commit was + google.protobuf.Timestamp mtimeConfig = 10; // mtime for the .git/config file } // this is probably better. think about moving to this instead