6
0
mirror of https://github.com/grdl/git-get.git synced 2026-02-06 05:17:58 +00:00
Files
git-get/new/status.go
Grzegorz Dlugoszewski 0b371341e7 Add branch status and tests
2020-05-27 20:30:04 +02:00

131 lines
2.7 KiB
Go

package new
import (
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/pkg/errors"
)
type RepoStatus struct {
HasUntrackedFiles bool
HasUncommittedChanges bool
Branches map[string]*BranchStatus
}
type BranchStatus struct {
Name string
Upstream *Upstream
NeedsPull bool
NeedsPush bool
Ahead int
Behind int
}
type Upstream struct {
Remote string
Branch string
}
func (r *Repo) LoadStatus() error {
wt, err := r.repo.Worktree()
if err != nil {
return errors.Wrap(err, "Failed getting worktree")
}
status, err := wt.Status()
if err != nil {
return errors.Wrap(err, "Failed getting worktree status")
}
r.Status.HasUncommittedChanges = !status.IsClean()
r.Status.HasUntrackedFiles = hasUntracked(status)
err = r.LoadBranchesStatus()
if err != nil {
return err
}
return nil
}
// hasUntracked returns true if there are any untracked files in the worktree
func hasUntracked(status git.Status) bool {
for _, fs := range status {
if fs.Worktree == git.Untracked {
return true
}
}
return false
}
func (r *Repo) LoadBranchesStatus() error {
iter, err := r.repo.Branches()
if err != nil {
return errors.Wrap(err, "Failed getting branches iterator")
}
err = iter.ForEach(func(reference *plumbing.Reference) error {
bs, err := r.newBranchStatus(reference.Name().Short())
if err != nil {
return err
}
r.Status.Branches[bs.Name] = bs
return nil
})
if err != nil {
return errors.Wrap(err, "Failed iterating over branches")
}
return nil
}
func (r *Repo) newBranchStatus(branch string) (*BranchStatus, error) {
upstream, err := r.upstream(branch)
if err != nil {
return nil, err
}
return &BranchStatus{
Name: branch,
Upstream: upstream,
}, nil
}
// upstream finds if a given branch tracks an upstream.
// Returns found upstream or nil if branch doesn't track an upstream.
//
// Information about upstream is taken from .git/config file.
// If a branch has an upstream, there's a [branch] section in the file with two fields:
// "remote" - name of the remote containing upstreamn branch (or "." if upstream is a local branch)
// "merge" - full ref name of the upstream branch (eg, ref/heads/master)
func (r *Repo) upstream(branch string) (*Upstream, error) {
cfg, err := r.repo.Config()
if err != nil {
return nil, errors.Wrap(err, "Failed getting repo config")
}
// Find our branch in "branch" config sections
bcfg := cfg.Branches[branch]
if bcfg == nil {
return nil, nil
}
remote := bcfg.Remote
if remote == "" {
return nil, nil
}
// TODO: check if this should be short or full ref name
merge := bcfg.Merge.Short()
if merge == "" {
return nil, nil
}
return &Upstream{
Remote: remote,
Branch: merge,
}, nil
}