594 lines
15 KiB
Go
594 lines
15 KiB
Go
package repostatus
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os/user"
|
|
"strings"
|
|
"time"
|
|
"unicode/utf8"
|
|
|
|
"io/ioutil"
|
|
|
|
"go.wit.com/log"
|
|
"go.wit.com/lib/gui/shell"
|
|
)
|
|
|
|
func (rs *RepoStatus) GetCurrentBranchName() string {
|
|
return rs.currentBranch.String()
|
|
}
|
|
|
|
func (rs *RepoStatus) GetCurrentBranchVersion() string {
|
|
return rs.currentVersion.String()
|
|
}
|
|
|
|
func (rs *RepoStatus) LastGitPull() (time.Time, error) {
|
|
return rs.mtime(".git/FETCH_HEAD")
|
|
}
|
|
|
|
func (rs *RepoStatus) Age() time.Duration {
|
|
var t *Tag
|
|
t = rs.NewestTag()
|
|
|
|
if t != nil {
|
|
log.Log(REPO, "newest tag:", t.date.String(), t.tag.String(), t.Name())
|
|
return t.Age()
|
|
}
|
|
|
|
const gitLayout = "Mon Jan 2 15:04:05 2006 -0700"
|
|
const madeuptime = "Mon Jun 3 15:04:05 2013 -0700"
|
|
tagTime, _ := time.Parse(gitLayout, madeuptime)
|
|
return time.Since(tagTime)
|
|
}
|
|
|
|
var ErrorMissingGitConfig error = errors.New("missing .git/config")
|
|
var ErrorGitPullOnLocal error = errors.New("git pull on local only branch")
|
|
|
|
func (rs *RepoStatus) GitPull() (string, error) {
|
|
currentName := rs.GetCurrentBranchName()
|
|
if rs.IsOnlyLocalTag(currentName) {
|
|
return "", ErrorGitPullOnLocal
|
|
}
|
|
var cmd []string
|
|
cmd = append(cmd, "git", "pull")
|
|
r := rs.Run(cmd)
|
|
output := strings.Join(r.Stdout, "\n")
|
|
if r.Error != nil {
|
|
output = "git error_,,,_a_,,,_b_,,,c"
|
|
}
|
|
if r.Error == nil {
|
|
log.Log(REPOWARN, "git pull ran", rs.Path())
|
|
log.Log(REPOWARN, "git pull output", output)
|
|
} else {
|
|
log.Log(REPOWARN, "git pull error", rs.Path(), r.Error)
|
|
}
|
|
return output, r.Error
|
|
}
|
|
|
|
/*
|
|
// this isn't right
|
|
func (rs *RepoStatus) LastTagAge() (time.Time, string) {
|
|
return time.Now(), rs.lasttag.String()
|
|
}
|
|
*/
|
|
|
|
func (rs *RepoStatus) GetLastTagVersion() string {
|
|
return rs.lasttag.String()
|
|
}
|
|
|
|
// stores the current branch name
|
|
func (rs *RepoStatus) checkCurrentBranchName() string {
|
|
currentname := rs.currentBranch.String()
|
|
out := run(rs.realPath.String(), "git", "branch --show-current")
|
|
if currentname == out {
|
|
// nothing changed
|
|
return currentname
|
|
}
|
|
rs.currentBranch.SetValue(out)
|
|
if currentname == "" {
|
|
return out // don't note if there was nothing before
|
|
}
|
|
rs.NoteChange("current branch has changed from " + currentname + " to " + out)
|
|
return out
|
|
}
|
|
|
|
func (rs *RepoStatus) gitDescribeByHash(hash string) (string, error) {
|
|
if hash == "" {
|
|
return "", errors.New("hash was blank")
|
|
}
|
|
r := shell.PathRunLog(rs.Path(), []string{"git", "describe", "--tags", "--always", hash}, INFO)
|
|
out := strings.Join(r.Stdout, "\n")
|
|
if r.Error != nil {
|
|
log.Warn("not in a git repo or bad hash?", r.Error, rs.Path())
|
|
return out, r.Error
|
|
}
|
|
return out, r.Error
|
|
}
|
|
|
|
func (rs *RepoStatus) gitDescribeByName(name string) (string, error) {
|
|
name = strings.TrimSpace(name)
|
|
|
|
if name == "" {
|
|
// git will return the current tag
|
|
r := shell.PathRunLog(rs.Path(), []string{"git", "describe", "--tags", "--always"}, INFO)
|
|
output := strings.Join(r.Stdout, "\n")
|
|
if r.Error != nil {
|
|
log.Warn("gitDescribeByName() not in a git repo?", r.Error, rs.Path())
|
|
}
|
|
return strings.TrimSpace(output), r.Error
|
|
}
|
|
if !rs.LocalTagExists(name) {
|
|
// tag does not exist
|
|
return "", errors.New("gitDescribeByName() git fatal: Not a valid object name")
|
|
}
|
|
cmd := []string{"git", "describe", "--tags", "--always", name}
|
|
r := shell.PathRunLog(rs.Path(), cmd, INFO)
|
|
output := strings.Join(r.Stdout, "\n")
|
|
if r.Error != nil {
|
|
log.Warn("cmd =", cmd)
|
|
log.Warn("err =", r.Error)
|
|
log.Warn("not in a git repo or bad tag?", rs.Path())
|
|
}
|
|
|
|
return strings.TrimSpace(output), r.Error
|
|
}
|
|
|
|
// todo: don't run git every time?
|
|
func (rs *RepoStatus) checkCurrentBranchVersion() string {
|
|
out, _ := rs.gitDescribeByName("")
|
|
log.Log(REPO, "checkCurrentBranchVersion()", out)
|
|
rs.currentVersion.SetValue(out)
|
|
return out
|
|
}
|
|
|
|
// this should get the most recent tag
|
|
func (rs *RepoStatus) setLastTagVersion() {
|
|
hash := run(rs.realPath.String(), "git", "rev-list --tags --max-count=1")
|
|
log.Log(REPO, "getLastTagVersion()", hash)
|
|
|
|
name, _ := rs.gitDescribeByHash(hash)
|
|
rs.lasttag.SetText(name)
|
|
return
|
|
}
|
|
|
|
func (rs *RepoStatus) populateTags() {
|
|
tmp := rs.realPath.String() + "/.git/refs/tags"
|
|
log.Log(REPO, "populateTags() path =", tmp)
|
|
for _, tag := range listFiles(tmp) {
|
|
if rs.tags[tag] == "" {
|
|
log.Log(REPO, "populateTags() Adding new tag", tag)
|
|
// rs.tagsDrop.AddText(tag)
|
|
rs.tags[tag] = "origin"
|
|
}
|
|
}
|
|
// rs.tagsDrop.SetText(rs.lasttagrev)
|
|
}
|
|
|
|
func (rs *RepoStatus) getBranches() []string {
|
|
var all []string
|
|
var heads []string
|
|
var remotes []string
|
|
heads = listFiles(rs.realPath.String() + "/.git/refs/heads")
|
|
remotes = listFiles(rs.realPath.String() + "/.git/refs/remotes")
|
|
|
|
all = heads
|
|
|
|
all = append(all, remotes...)
|
|
|
|
for _, branch := range all {
|
|
log.Log(REPO, "getBranches()", branch)
|
|
}
|
|
return all
|
|
}
|
|
|
|
// returns quickly based on the last time it was checked
|
|
func (rs *RepoStatus) IsDirty() bool {
|
|
if rs.dirtyLabel.String() == "no" {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// return the list of dirty files (but ignores go.mod & go.sum)
|
|
func (rs *RepoStatus) DirtyList() []string {
|
|
var all []string
|
|
for _, line := range strings.Split(rs.dirtyList, "\n") {
|
|
line = strings.TrimSpace(line)
|
|
parts := strings.Split(line, " ")
|
|
if len(parts) != 2 {
|
|
continue
|
|
}
|
|
if parts[1] == "go.mod" {
|
|
continue
|
|
}
|
|
if parts[1] == "go.sum" {
|
|
continue
|
|
}
|
|
all = append(all, parts[1])
|
|
}
|
|
return all
|
|
}
|
|
|
|
func (rs *RepoStatus) CheckDirty() bool {
|
|
var start string = rs.dirtyLabel.String()
|
|
cmd := []string{"git", "status", "--porcelain"}
|
|
r := shell.PathRunLog(rs.Path(), cmd, INFO)
|
|
out := strings.Join(r.Stdout, "\n")
|
|
if r.Error != nil {
|
|
log.Warn("CheckDirty() status cmd =", cmd)
|
|
log.Warn("CheckDirty() status out =", out)
|
|
log.Warn("CheckDirty() status err =", r.Error)
|
|
log.Error(r.Error, "CheckDirty() git status error")
|
|
rs.dirtyLabel.SetValue("error")
|
|
if start != "error" {
|
|
rs.NoteChange("git status is in error " + fmt.Sprint(r.Error))
|
|
}
|
|
return true
|
|
}
|
|
|
|
rs.dirtyList = out
|
|
|
|
// last := out[strings.LastIndex(out, "\n")+1:]
|
|
// if last == "nothing to commit, working tree clean" {
|
|
|
|
if len(rs.DirtyList()) == 0 {
|
|
log.Log(REPO, "CheckDirty() no", rs.realPath.String())
|
|
rs.dirtyLabel.SetValue("no")
|
|
if start == "" {
|
|
// don't record a change as this is the initial run
|
|
return false
|
|
}
|
|
if start != "no" {
|
|
log.Log(REPOWARN, "is no longer dirty")
|
|
rs.NoteChange("is no longer dirty")
|
|
}
|
|
return false
|
|
}
|
|
|
|
rs.dirtyLabel.SetValue("dirty")
|
|
if start == "" {
|
|
// don't record a change as this is the initial run
|
|
return false
|
|
}
|
|
if start != "dirty" {
|
|
log.Log(REPOWARN, "is now dirty")
|
|
rs.NoteChange("is now dirty")
|
|
}
|
|
return true
|
|
|
|
}
|
|
func (rs *RepoStatus) CheckoutBranch(bname string) bool {
|
|
if rs.CheckDirty() {
|
|
log.Log(REPO, rs.realPath.String(), "is dirty")
|
|
log.Info(bname, "is dirty", rs.Path())
|
|
return false
|
|
}
|
|
if !rs.TagExists(bname) {
|
|
// tag does not exist
|
|
log.Log(REPO, "repo does not have branch", bname, rs.Path())
|
|
return false
|
|
}
|
|
cName := rs.GetCurrentBranchName()
|
|
if cName == bname {
|
|
// already on branch
|
|
return true
|
|
}
|
|
cmd := []string{"git", "checkout", bname}
|
|
r := rs.Run(cmd)
|
|
if r.Error != nil {
|
|
log.Log(REPO, "git checkout error:", r.Error)
|
|
return false
|
|
}
|
|
rs.checkCurrentBranchName()
|
|
rs.checkCurrentBranchVersion()
|
|
return true
|
|
}
|
|
|
|
func (rs *RepoStatus) CheckoutMaster() bool {
|
|
if rs.CheckDirty() {
|
|
log.Log(REPO, rs.realPath.String(), "is dirty")
|
|
return false
|
|
}
|
|
mName := rs.GetMasterBranchName()
|
|
if rs.CheckoutBranch(mName) {
|
|
return true
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (rs *RepoStatus) CheckoutDevel() bool {
|
|
devel := rs.develWorkingName.String()
|
|
// user := rs.userWorkingName.String()
|
|
if devel == "" {
|
|
return false
|
|
}
|
|
if rs.CheckDirty() {
|
|
log.Log(REPO, rs.realPath.String(), "is dirty")
|
|
return false
|
|
}
|
|
|
|
log.Log(REPO, "checkoutBranch", devel)
|
|
rs.checkoutBranch("devel", devel)
|
|
// log.Log(REPO, "checkoutBranch", user)
|
|
// rs.checkoutBranch("user", user)
|
|
return true
|
|
}
|
|
|
|
func (rs *RepoStatus) CheckoutUser() bool {
|
|
bName := rs.GetUserBranchName()
|
|
if bName == "" {
|
|
return false
|
|
}
|
|
if rs.CheckDirty() {
|
|
log.Log(REPO, rs.realPath.String(), "is dirty")
|
|
return false
|
|
}
|
|
if !rs.BranchExists(bName) {
|
|
return false
|
|
}
|
|
cmd := []string{"git", "checkout", bName}
|
|
r := rs.Run(cmd)
|
|
if r.Error != nil {
|
|
log.Log(REPO, "git checkout error:", r.Error)
|
|
}
|
|
|
|
realname := rs.GetCurrentBranchName()
|
|
realversion := rs.GetCurrentBranchVersion()
|
|
log.Log(REPO, rs.realPath.String(), "realname =", realname, "realversion =", realversion)
|
|
|
|
if realname != bName {
|
|
log.Log(REPO, "git checkout failed", rs.realPath.String(), bName, "!=", realname)
|
|
return false
|
|
}
|
|
rs.userBranchVersion.SetValue(realversion)
|
|
return true
|
|
}
|
|
|
|
func (rs *RepoStatus) checkoutBranch(level string, branch string) {
|
|
if rs.CheckDirty() {
|
|
log.Log(REPO, "checkoutBranch() checkDirty() == true for repo", rs.realPath.String(), "looking for branch:", branch)
|
|
return
|
|
}
|
|
out := run(rs.realPath.String(), "git", "checkout "+branch)
|
|
log.Log(REPO, rs.realPath.String(), "git checkout "+branch, "returned", out)
|
|
|
|
realname := rs.GetCurrentBranchName()
|
|
realversion := rs.GetCurrentBranchVersion()
|
|
log.Log(REPO, rs.realPath.String(), "realname =", realname, "realversion =", realversion)
|
|
|
|
switch level {
|
|
case "master":
|
|
rs.mainBranchVersion.SetValue(realversion)
|
|
case "devel":
|
|
rs.develBranchVersion.SetValue(realversion)
|
|
case "user":
|
|
rs.userBranchVersion.SetValue(realversion)
|
|
default:
|
|
}
|
|
}
|
|
|
|
// attempt's to guess at what master is.
|
|
// TODO: fix this properly
|
|
func (rs *RepoStatus) guessMainWorkingName() {
|
|
if !rs.Ready() {
|
|
return
|
|
}
|
|
if rs.TagExists("guimaster") {
|
|
rs.mainWorkingName.SetText("guimaster")
|
|
rs.mainBranchVersion.SetLabel("guimaster")
|
|
return
|
|
}
|
|
if rs.TagExists("master") {
|
|
rs.mainWorkingName.SetText("master")
|
|
rs.mainBranchVersion.SetLabel("master")
|
|
return
|
|
}
|
|
if rs.TagExists("main") {
|
|
rs.mainWorkingName.SetText("main")
|
|
rs.mainBranchVersion.SetLabel("main")
|
|
return
|
|
}
|
|
|
|
// figure out what to do here
|
|
rs.mainWorkingName.SetText("FIXME")
|
|
rs.mainBranchVersion.SetLabel("FIXME")
|
|
}
|
|
|
|
func (rs *RepoStatus) guessDevelWorkingName() {
|
|
if rs.TagExists("guidevel") {
|
|
rs.develWorkingName.SetValue("guidevel")
|
|
rs.develBranchVersion.SetLabel("guidevel")
|
|
return
|
|
}
|
|
if rs.TagExists("devel") {
|
|
rs.develWorkingName.SetValue("devel")
|
|
rs.develBranchVersion.SetLabel("devel")
|
|
return
|
|
}
|
|
|
|
// figure out what to do here
|
|
rs.develWorkingName.SetValue("develFIXME")
|
|
rs.develBranchVersion.SetLabel("develFIXME")
|
|
}
|
|
|
|
func (rs *RepoStatus) setUserWorkingName() {
|
|
usr, _ := user.Current()
|
|
uname := usr.Username
|
|
if rs.TagExists(uname) {
|
|
rs.userWorkingName.SetValue(uname)
|
|
rs.userBranchVersion.SetLabel(uname)
|
|
return
|
|
}
|
|
rs.userWorkingName.SetValue("need to create " + uname)
|
|
rs.userBranchVersion.SetLabel("need to create " + uname)
|
|
}
|
|
|
|
// returns "master", "devel", os.Username, etc
|
|
func (rs *RepoStatus) GetMasterBranchName() string {
|
|
name := rs.mainWorkingName.String()
|
|
return name
|
|
}
|
|
func (rs *RepoStatus) GetDevelBranchName() string {
|
|
name := rs.develWorkingName.String()
|
|
return name
|
|
}
|
|
|
|
func (rs *RepoStatus) GetUserBranchName() string {
|
|
name := rs.userWorkingName.String()
|
|
return name
|
|
}
|
|
|
|
// returns the git versions like "1.3-2-laksdjf" or whatever
|
|
func (rs *RepoStatus) GetMasterVersion() string {
|
|
name := rs.mainBranchVersion.String()
|
|
return name
|
|
}
|
|
func (rs *RepoStatus) GetDevelVersion() string {
|
|
name := rs.develBranchVersion.String()
|
|
return name
|
|
}
|
|
func (rs *RepoStatus) GetUserVersion() string {
|
|
name := rs.userBranchVersion.String()
|
|
return name
|
|
}
|
|
|
|
func (rs *RepoStatus) setMasterVersion(s string) {
|
|
old := rs.GetMasterVersion()
|
|
if old == s {
|
|
return
|
|
}
|
|
rs.mainBranchVersion.SetValue(s)
|
|
if old == "" {
|
|
return // don't note if there was nothing before
|
|
}
|
|
rs.NoteChange("master branch has been changed from " + old + " to " + s)
|
|
}
|
|
|
|
func (rs *RepoStatus) setDevelVersion(s string) {
|
|
old := rs.GetDevelVersion()
|
|
if old == s {
|
|
return
|
|
}
|
|
if old == "" {
|
|
// don't note nothing
|
|
} else {
|
|
rs.NoteChange("devel branch has been changed from " + old + " to " + s)
|
|
}
|
|
rs.develBranchVersion.SetValue(s)
|
|
}
|
|
|
|
func (rs *RepoStatus) setUserVersion(s string) {
|
|
old := rs.GetUserVersion()
|
|
if old == s {
|
|
return
|
|
}
|
|
if old == "" {
|
|
// don't note nothing
|
|
} else {
|
|
rs.NoteChange("user branch has been changed from " + old + " to " + s)
|
|
}
|
|
rs.userBranchVersion.SetValue(s)
|
|
}
|
|
|
|
func (rs *RepoStatus) GitState() string {
|
|
return rs.gitState.String()
|
|
}
|
|
|
|
func (rs *RepoStatus) CheckGitState() string {
|
|
rs.setState()
|
|
return rs.gitState.String()
|
|
}
|
|
|
|
func (rs *RepoStatus) GetStatus() string {
|
|
return rs.gitState.String()
|
|
}
|
|
|
|
func (rs *RepoStatus) setState() {
|
|
rs.changed = false
|
|
if rs.CheckDirty() {
|
|
log.Log(REPO, "CheckDirty() true")
|
|
rs.gitState.SetText("dirty")
|
|
return
|
|
}
|
|
if rs.GetUserVersion() != rs.GetDevelVersion() {
|
|
rs.gitState.SetText("merge to devel")
|
|
return
|
|
}
|
|
if rs.GetDevelVersion() != rs.GetMasterVersion() {
|
|
rs.gitState.SetText("merge to main")
|
|
return
|
|
}
|
|
if rs.lasttag.String() != rs.GetMasterVersion() {
|
|
rs.gitState.SetText("unchanged")
|
|
return
|
|
}
|
|
|
|
if rs.CheckBranches() {
|
|
log.Log(REPO, "Branches are Perfect")
|
|
rs.gitState.SetText("PERFECT")
|
|
return
|
|
}
|
|
log.Log(REPO, rs.String(), "Branches are not Perfect")
|
|
rs.gitState.SetText("unknown branches")
|
|
}
|
|
|
|
// TODO: make this report the error somewhere
|
|
// This is supposed to check all the branches to make sure
|
|
// the are the same. that was originally what this was for
|
|
// now I think it's jsut probably dumb old code that doesn't
|
|
// need to be here
|
|
|
|
// actually, this is to attempt to verify absolutely everything
|
|
// is pushed upstream before doing a rm -rf ~/go/src
|
|
// TODO: revisit this code in the autotypist later
|
|
func (rs *RepoStatus) CheckBranches() bool {
|
|
var hashCheck string
|
|
var perfect bool = true
|
|
all := rs.getBranches()
|
|
path := rs.realPath.String() + "/.git/refs/"
|
|
for _, b := range all {
|
|
parts := strings.Split(b, "/")
|
|
rdir := "heads"
|
|
if len(parts) == 2 {
|
|
rdir = "remotes"
|
|
}
|
|
fullfile := path + "/" + rdir + "/" + b
|
|
|
|
// check if the ref name is "HEAD". if so, skip
|
|
runeCount := utf8.RuneCountInString(fullfile)
|
|
// Convert the string to a slice of runes
|
|
runes := []rune(fullfile)
|
|
// Slice the last 4 runes
|
|
lastFour := runes[runeCount-4:]
|
|
if string(lastFour) == "HEAD" {
|
|
log.Log(REPO, "skip HEAD fullfile", fullfile)
|
|
continue
|
|
}
|
|
|
|
content, _ := ioutil.ReadFile(fullfile)
|
|
hash := strings.TrimSpace(string(content))
|
|
if hashCheck == "" {
|
|
hashCheck = hash
|
|
}
|
|
var cmd []string
|
|
cmd = append(cmd, "git", "show", "-s", "--format=%ci", hash)
|
|
r := shell.PathRunLog(rs.Path(), cmd, INFO)
|
|
if r.Error != nil {
|
|
log.Log(WARN, "CheckBranches() git show error:", r.Error)
|
|
}
|
|
// git show -s --format=%ci <hash> will give you the time
|
|
// log.Log(REPO, fullfile)
|
|
if hash == hashCheck {
|
|
log.Log(REPO, "notsure why this git show is here", hash)
|
|
} else {
|
|
// log.Log(WARN, rs.String(), hash, output, b)
|
|
// log.Log(WARN, "UNKNOWN BRANCHES IN THIS REPO", cmd)
|
|
rs.versionCmdOutput.SetText("UNKNOWN BRANCHES")
|
|
perfect = false
|
|
// parts := strings.Split(b, "/")
|
|
// log.Warn("git push", parts)
|
|
}
|
|
}
|
|
return perfect
|
|
}
|