package repostatus import ( "errors" "strings" "time" "unicode/utf8" "io/ioutil" "go.wit.com/log" ) func (rs *RepoStatus) GetCurrentBranchName() string { return rs.currentBranch.String() } func (rs *RepoStatus) GetCurrentBranchVersion() string { return rs.currentVersion.String() } 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) getCurrentBranchName() string { return rs.currentBranch.String() } func (rs *RepoStatus) gitDescribeByHash(hash string) (string, error) { if hash == "" { return "", errors.New("hash was blank") } err, out := rs.RunCmd([]string{"git", "describe", "--tags", "--always", hash}) if err != nil { log.Warn("not in a git repo or bad hash?", err, rs.Path()) return "", err } out = strings.TrimSpace(out) return out, err } func (rs *RepoStatus) gitDescribeByName(name string) (string, error) { name = strings.TrimSpace(name) if name == "" { // git will return the current tag err, out := rs.RunCmd([]string{"git", "describe", "--tags", "--always"}) if err != nil { log.Warn("not in a git repo?", err, rs.Path()) return "", err } out = strings.TrimSpace(out) return out, err } if !rs.LocalTagExists(name) { // tag does not exist return "", errors.New("git fatal: Not a valid object name") } cmd := []string{"git", "describe", "--tags", "--always", name} err, out := rs.RunCmd(cmd) if err != nil { log.Warn("cmd =", cmd) log.Warn("err =", err) log.Warn("not in a git repo or bad tag?", rs.Path()) return "", err } out = strings.TrimSpace(out) return out, err } // todo: don't run git every time? func (rs *RepoStatus) checkCurrentBranchVersion() string { out, _ := rs.gitDescribeByName("") log.Log(INFO, "checkCurrentBranchVersion()", out) rs.currentVersion.SetValue(out) return out } func (rs *RepoStatus) getCurrentBranchVersion() string { return rs.currentVersion.String() } // 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(INFO, "getLastTagVersion()", hash) name, _ := rs.gitDescribeByHash(hash) rs.lasttag.SetText(name) return } func (rs *RepoStatus) populateTags() { tmp := rs.realPath.String() + "/.git/refs/tags" log.Log(INFO, "populateTags() path =", tmp) for _, tag := range listFiles(tmp) { if rs.tags[tag] == "" { log.Log(INFO, "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(INFO, "getBranches()", branch) } return all } func (rs *RepoStatus) CheckDirty() bool { cmd := []string{"git", "status"} path := rs.realPath.String() err, b, out := RunCmd(path, cmd) if err != nil { log.Warn("CheckDirty() status b =", b) log.Warn("CheckDirty() status cmd =", cmd) log.Warn("CheckDirty() status path =", path) log.Warn("CheckDirty() status out =", out) log.Warn("CheckDirty() status err =", err) log.Error(err, "CheckDirty() git status error") rs.dirtyLabel.SetValue("error") return true } last := out[strings.LastIndex(out, "\n")+1:] if last == "nothing to commit, working tree clean" { log.Log(INFO, "CheckDirty() b =", b, "path =", path, "out =", out) log.Log(INFO, "CheckDirty() no", rs.realPath.String()) rs.dirtyLabel.SetValue("no") return false } // sometimes b gets exit status 1 when there isn't anything that has changed // run git status fixes that for some reason. log.Log(INFO, "CheckDirty() is normal dirty", rs.realPath.String()) log.Log(INFO, "CheckDirty() is normal cmd =", cmd) log.Log(INFO, "CheckDirty() is normal b =", b) log.Log(INFO, "CheckDirty() is normal path =", path) log.Log(INFO, "CheckDirty() is normal out =", out) log.Log(INFO, "CheckDirty() is normal err =", err) rs.dirtyLabel.SetValue("dirty") return true } func (rs *RepoStatus) CheckoutBranch(bname string) bool { if rs.CheckDirty() { log.Log(INFO, rs.realPath.String(), "is dirty") log.Info("bname is dirty", bname, rs.Path()) return false } if !rs.TagExists(bname) { // tag does not exist log.Info("bname already exists", bname, rs.Path()) return false } cName := rs.GetCurrentBranchName() if cName == bname { // already on branch return true } cmd := []string{"git", "checkout", bname} err, b, output := RunCmd(rs.realPath.String(), cmd) if err != nil { log.Log(INFO, err, b, output) return false } rs.checkCurrentBranchName() rs.checkCurrentBranchVersion() return true } func (rs *RepoStatus) CheckoutMaster() bool { if rs.CheckDirty() { log.Log(INFO, rs.realPath.String(), "is dirty") return false } mName := rs.GetMasterBranchName() cmd := []string{"git", "checkout", mName} err, b, output := RunCmd(rs.realPath.String(), cmd) if err != nil { log.Log(INFO, err, b, output) } realname := rs.getCurrentBranchName() realversion := rs.getCurrentBranchVersion() log.Log(INFO, rs.realPath.String(), "realname =", realname, "realversion =", realversion) if realname != mName { log.Log(INFO, "git checkout failed", rs.realPath.String(), mName, "!=", realname) return false } rs.masterBranchVersion.SetValue(realversion) return true } func (rs *RepoStatus) CheckoutDevel() bool { devel := rs.develWorkingName.String() // user := rs.userWorkingName.String() if devel == "" { return false } if rs.CheckDirty() { log.Log(INFO, rs.realPath.String(), "is dirty") return false } log.Log(INFO, "checkoutBranch", devel) rs.checkoutBranch("devel", devel) // log.Log(INFO, "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(INFO, rs.realPath.String(), "is dirty") return false } cmd := []string{"git", "checkout", bName} err, b, output := RunCmd(rs.realPath.String(), cmd) if err != nil { log.Log(INFO, err, b, output) } realname := rs.getCurrentBranchName() realversion := rs.getCurrentBranchVersion() log.Log(INFO, rs.realPath.String(), "realname =", realname, "realversion =", realversion) if realname != bName { log.Log(INFO, "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(INFO, "checkoutBranch() checkDirty() == true for repo", rs.realPath.String(), "looking for branch:", branch) return } out := run(rs.realPath.String(), "git", "checkout "+branch) log.Log(INFO, rs.realPath.String(), "git checkout "+branch, "returned", out) realname := rs.getCurrentBranchName() realversion := rs.getCurrentBranchVersion() log.Log(INFO, rs.realPath.String(), "realname =", realname, "realversion =", realversion) switch level { case "master": rs.masterBranchVersion.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) setMainWorkingName(s string) { if rs == nil { log.Info("rs == nil", s) return } if rs.gitConfig == nil { log.Info("rs.gitConfig == nil", s) rs.mainWorkingName.SetValue(s) return } if rs.gitConfig.branches == nil { log.Info("rs.gitConfig.branches == nil", s) rs.mainWorkingName.SetValue(s) return } _, ok := rs.gitConfig.branches[s] if ok { // log.Info("git branch", s, "seems to exist") rs.mainWorkingName.SetValue(s) return } s = "guimaster" _, ok = rs.gitConfig.branches[s] if ok { // log.Info("git branch", s, "seems to exist") rs.mainWorkingName.SetValue(s) return } s = "master" _, ok = rs.gitConfig.branches[s] if ok { // log.Info("git branch", s, "seems to exist") rs.mainWorkingName.SetValue(s) return } s = "main" _, ok = rs.gitConfig.branches[s] if ok { // log.Info("git branch", s, "seems to exist") rs.mainWorkingName.SetValue(s) return } s = "TODO: read .git/config" log.Warn("git branch", s, "does not seem to exist. TODO: figure out the server default") for name, _ := range rs.gitConfig.branches { log.Warn("git branch found. use this?", name) } rs.mainWorkingName.SetValue(s) } func (rs *RepoStatus) setDevelWorkingName(s string) { rs.develWorkingName.SetValue(s) rs.develBranchVersion.SetLabel(s) } func (rs *RepoStatus) setUserWorkingName(s string) { rs.userWorkingName.SetValue(s) rs.userBranchVersion.SetLabel(s) } // 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.masterBranchVersion.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.masterBranchVersion.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) GetStatus() string { rs.changed = false if rs.CheckDirty() { log.Log(INFO, "CheckDirty() true") return "dirty" } if rs.userBranchVersion.String() != rs.develBranchVersion.String() { return "merge to devel" } if rs.develBranchVersion.String() != rs.masterBranchVersion.String() { return "merge to main" } if rs.lasttag.String() != rs.masterBranchVersion.String() { return "ready to tag version" } if rs.CheckBranches() { log.Log(INFO, "Branches are Perfect") return "PERFECT" } log.Log(INFO, rs.String(), "Branches are not Perfect") return "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(INFO, "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) err, output := rs.RunCmd(cmd) if err != nil { // log.Log(WARN, "cmd failed", cmd, "err =", err, "in", rs.String()) } // git show -s --format=%ci will give you the time // log.Log(INFO, fullfile) if hash == hashCheck { log.Log(INFO, hash, output, b) } 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 }