parse .git/config
This commit is contained in:
parent
80f602c4d9
commit
0c30a9da2f
19
gitTag.proto
19
gitTag.proto
|
@ -4,6 +4,25 @@ package gitpb;
|
||||||
|
|
||||||
import "google/protobuf/timestamp.proto"; // Import the well-known type for Timestamp
|
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<string, string> core = 1; // map[origin] = "https:/git.wit.org/gui/gadgets"
|
||||||
|
map<string, GitRemote> remotes = 2; // map[origin] = "https:/git.wit.org/gui/gadgets"
|
||||||
|
map<string, GitBranch> branches = 3; // map[guimaster] = origin guimaster
|
||||||
|
map<string, string> submodules = 4;
|
||||||
|
map<string, string> hashes = 5;
|
||||||
|
map<string, string> versions = 6;
|
||||||
|
}
|
||||||
|
|
||||||
message GitTag { // `autogenpb:nomutex`
|
message GitTag { // `autogenpb:nomutex`
|
||||||
string refname = 1; // `autogenpb:unique` `autogenpb:sort` // tag name. treated as unique
|
string refname = 1; // `autogenpb:unique` `autogenpb:sort` // tag name. treated as unique
|
||||||
|
|
|
@ -28,6 +28,12 @@ func (repo *Repo) Reload() error {
|
||||||
repo.CheckDirty()
|
repo.CheckDirty()
|
||||||
repo.setRepoState()
|
repo.setRepoState()
|
||||||
|
|
||||||
|
if repo.GitConfig == nil {
|
||||||
|
if err := repo.updateGitConfig(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// LastUpdate should always be the newest time
|
// LastUpdate should always be the newest time
|
||||||
repo.Times.LastUpdate = timestamppb.New(time.Now())
|
repo.Times.LastUpdate = timestamppb.New(time.Now())
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -119,6 +119,31 @@ func (repo *Repo) changedHead() bool {
|
||||||
return true
|
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 {
|
func (repo *Repo) changedIndex() bool {
|
||||||
fname := ".git/index"
|
fname := ".git/index"
|
||||||
fileTime := repo.Mtime(fname)
|
fileTime := repo.Mtime(fname)
|
||||||
|
@ -154,6 +179,9 @@ func (repo *Repo) reloadMtimes() bool {
|
||||||
if repo.changedIndex() {
|
if repo.changedIndex() {
|
||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
|
if repo.changedConfig() {
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
if repo.changedDir() {
|
if repo.changedDir() {
|
||||||
// changed = true
|
// changed = true
|
||||||
}
|
}
|
||||||
|
@ -171,6 +199,9 @@ func (repo *Repo) DidRepoChange() bool {
|
||||||
if repo.didFileChange(".git/index", repo.Times.MtimeIndex) {
|
if repo.didFileChange(".git/index", repo.Times.MtimeIndex) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
if repo.didFileChange(".git/config", repo.Times.MtimeConfig) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
if repo.didFileChange(".git", repo.Times.MtimeDir) {
|
if repo.didFileChange(".git", repo.Times.MtimeDir) {
|
||||||
// todo: do something with CheckDirty()
|
// todo: do something with CheckDirty()
|
||||||
// return true
|
// return true
|
||||||
|
|
|
@ -135,7 +135,13 @@ func (repo *Repo) NewestTag() *GitTag {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this should just do is.Exists(".git/refs/heads/findname")
|
||||||
func (repo *Repo) LocalTagExists(findname string) bool {
|
func (repo *Repo) LocalTagExists(findname string) bool {
|
||||||
|
fname := filepath.Join(".git/refs/heads", findname)
|
||||||
|
if repo.Exists(fname) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
/*
|
||||||
loop := repo.Tags.SortByRefname()
|
loop := repo.Tags.SortByRefname()
|
||||||
for loop.Scan() {
|
for loop.Scan() {
|
||||||
ref := loop.Next()
|
ref := loop.Next()
|
||||||
|
@ -143,13 +149,14 @@ func (repo *Repo) LocalTagExists(findname string) bool {
|
||||||
if strings.HasPrefix(ref.Refname, "refs/remotes") {
|
if strings.HasPrefix(ref.Refname, "refs/remotes") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
_, tagname := filepath.Split(ref.Refname)
|
tagname := filepath.Base(ref.Refname)
|
||||||
// log.Info("tag:", path, tagname, "from", repo.GoPath)
|
// log.Info("tag:", path, tagname, "from", repo.GoPath)
|
||||||
if tagname == findname {
|
if tagname == findname {
|
||||||
// log.Info("found tag:", path, tagname, "from", repo.GoPath)
|
// log.Info("found tag:", path, tagname, "from", repo.GoPath)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ message Repo { // `autogenpb:marshal` `autogenpb:2nomutex`
|
||||||
repeated string dirtyList = 22; // store the list from git status --porcelain
|
repeated string dirtyList = 22; // store the list from git status --porcelain
|
||||||
string state = 23; // status or state. useful for building tooling
|
string state = 23; // status or state. useful for building tooling
|
||||||
GitTag currentTag = 24; // used to examine repo branches
|
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`
|
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 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 lastGoDep = 8; // mtime for last go.sum scan
|
||||||
google.protobuf.Timestamp newestCommit = 9; // when the newest commit was
|
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
|
// this is probably better. think about moving to this instead
|
||||||
|
|
Loading…
Reference in New Issue